package com.dayatang.hrm.compensation.domain;

import java.io.InputStream;
import java.io.Reader;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;

import com.dayatang.commons.domain.AbstractEntity;
import com.dayatang.commons.domain.InstanceFactory;
import com.dayatang.hrm.compensation.CompensationConstant;
import com.dayatang.hrm.compensation.context.CompensationMessageSource;
import com.dayatang.hrm.compensation.domain.exception.CompensationRuntimeException;
import com.dayatang.hrm.compensation.service.CalculateRuleTranslator;
import com.dayatang.hrm.staffing.domain.Employee;
import com.dayatang.utils.DateUtils;

/**
 * 计算规则
 * 
 * @author chencao
 * 
 */
@Entity
@Table(name = "pay_cal_rule")
@NamedQueries( { @NamedQuery(name = "CalculateRule.findBySalaryCalculateItem", query = "select new CalculateRule(rule.abolishDate,setting,rule.calItem) from CalculateRuleSetting setting inner join setting.calculateRule rule inner join rule.calItem item where (item = :pItem and rule.abolishDate >= :pQueryDate and setting.fromDate <= :pQueryDate and setting.toDate >= :pQueryDate)") })
public class CalculateRule extends AbstractEntity {

	public static String DATE_FORMAT_PATTERN = CompensationConstant.DATE_FORMAT_PATTERN;

	/**
	 * 
	 */
	private static final long serialVersionUID = 7687929758153303973L;

	private static CalculateRuleRepository calculateRuleRepository;

	private static CalculateRuleTranslator ruleTranslator;

	protected static MessageSourceAccessor messages = CompensationMessageSource
			.getAccessor();

	@ManyToOne
	@JoinColumn(name = "salary_cal_item_id")
	private SalaryCalculateItem calItem;

	// 废除日期
	@Column(name = "abolish_date")
	@Temporal(TemporalType.TIMESTAMP)
	private Date abolishDate = DateUtils.MAX_DATE;

	/*
	 * =======================================
	 * 
	 * transient
	 * 
	 * ========================================
	 */
	/**
	 * latestSetting为薪资公式具体设置的一个代理
	 */
	@Transient
	private CalculateRuleSetting latestSetting;

	public CalculateRule() {
		if (latestSetting == null) {
			latestSetting = CalculateRuleSetting.init(this);
		}
	}

	public CalculateRule(Date abolishDate, CalculateRuleSetting latestSetting,
			SalaryCalculateItem calItem) {
		this.abolishDate = abolishDate;
		this.latestSetting = latestSetting;
		this.calItem = calItem;
	}

	public static CalculateRuleRepository getCalculateRuleRepository() {
		if (calculateRuleRepository == null) {
			calculateRuleRepository = InstanceFactory
					.getInstance(CalculateRuleRepository.class);
		}
		return calculateRuleRepository;
	}

	public static void setCalculateRuleRepository(
			CalculateRuleRepository calculateRuleRepository) {
		CalculateRule.calculateRuleRepository = calculateRuleRepository;
	}

	public static MessageSourceAccessor getMessages() {
		return messages;
	}

	public static void setMessages(MessageSourceAccessor messages) {
		CalculateRule.messages = messages;
	}

	public static CalculateRuleTranslator getRuleTranslator() {
		return ruleTranslator;
	}

	public static void setRuleTranslator(CalculateRuleTranslator ruleTranslator) {
		CalculateRule.ruleTranslator = ruleTranslator;
	}

	public Date getAbolishDate() {
		return abolishDate;
	}

	public void setAbolishDate(Date abolishDate) {
		this.abolishDate = abolishDate;
	}

	public SalaryCalculateItem getCalItem() {
		return calItem;
	}

	public void setCalItem(SalaryCalculateItem calItem) {
		if (isNew()) {
			this.calItem = calItem;
		} else {
			throw new CompensationRuntimeException(messages.getMessage(
					"CalculateRule.setCalItem.couldNotModify",
					"不能修改薪资公式所属的薪资计算项目。"));
		}
	}

	CalculateRuleSetting getLatestSetting() {
		return latestSetting;
	}

	void setLatestSetting(CalculateRuleSetting latestSetting) {
		this.latestSetting = latestSetting;
	}

	// 代理属性

	public String getAttributeText() {
		return latestSetting.getAttributeText();
	}

	public void setAttributeText(String attributeText) {
		latestSetting.setAttributeText(attributeText);
	}

	public String getConditionText() {
		return latestSetting.getConditionText();
	}

	public void setConditionText(String conditionText) {
		latestSetting.setConditionText(conditionText);
	}

	public String getConsequenceText() {
		return latestSetting.getConsequenceText();
	}

	public void setConsequenceText(String consequenceText) {
		latestSetting.setConsequenceText(consequenceText);
	}

	public Boolean isError() {
		return latestSetting.getError();
	}

	public Boolean getError() {
		return latestSetting.getError();
	}

	public void setError(Boolean error) {
		latestSetting.setError(error);
	}

	public Integer getSequence() {
		return latestSetting.getSequence();
	}

	public void setSequence(Integer sequence) {
		latestSetting.setSequence(sequence);
	}

	public String getRemark() {
		return latestSetting.getRemark();
	}

	public void setRemark(String remark) {
		latestSetting.setRemark(remark);
	}

	public Employee getOperator() {
		return latestSetting.getOperator();
	}

	public void setOperator(Employee operator) {
		latestSetting.setOperator(operator);
	}

	public Date getCreated() {
		return latestSetting.getCreated();
	}

	public void setCreated(Date created) {
		latestSetting.setCreated(created);
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof CalculateRule == false) {
			return false;
		}
		if (this == obj) {
			return true;
		}
		CalculateRule rhs = (CalculateRule) obj;
		return new EqualsBuilder().append(getId(), rhs.getId()).isEquals();
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder(17, 37).append(getId()).toHashCode();
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this,
				ToStringStyle.SHORT_PREFIX_STYLE);
	}

	/*
	 * =======================================
	 * 
	 * action
	 * 
	 * ========================================
	 */

	private void save(boolean needValidateRule) {
		if (needValidateRule) {
			validateCalculateRule();
		}

		getCalculateRuleRepository().save(this);
	}

	/**
	 * 改正设置并保存（不会将设置备份）
	 */
	public void saveChanges(boolean needValidateRule) {
		save(needValidateRule);
		latestSetting.saveChanges();
	}

	/**
	 * 备份原有设置并保存当前设置（会将之前的设置备份）
	 */
	public void archive(boolean needValidateRule) {
		save(needValidateRule);
		latestSetting.archive();
	}

	public void markArchive(boolean needValidateRule) {
		if (needValidateRule) {
			validateCalculateRule();
		}

		latestSetting.markArchive();

	}

	private void validateCalculateRule() {
		getCalculateRuleRepository().validateCalculateRule(this);
	}

	void remove() {
		getCalculateRuleRepository().remove(this);
	}

	public void abolish(Date abolishDate, boolean needValidateRule) {
		if (abolishDate == null) {
			abolishDate = new Date();
		}
		setAbolishDate(abolishDate);
		save(needValidateRule);

		latestSetting.abolish(abolishDate);

	}

	public List<SalaryParameter> getDependentSalaryParameters(Date queryDate) {
		return getCalculateRuleRepository().getDependentSalaryParameters(this,
				queryDate);
	}

	public List<SalaryCalculateItem> getDependentSalaryCalculateItems(
			Date queryDate) {
		return getCalculateRuleRepository().getDependentSalaryCalculateItems(
				this, queryDate);
	}

	public static CalculateRule get(Long id, Date queryDate) {
		CalculateRule rule = getCalculateRuleRepository().get(id);
		if (rule != null) {
			CalculateRuleSetting setting = CalculateRuleSetting.get(rule,
					queryDate);
			if (setting == null) {
				// setting = CalculateRuleSetting.init(rule);
				throw new CompensationRuntimeException(messages.getMessage(
						"CalculateRule.get.notExist", "该日期下的薪资公式不存在。"));
			}
			rule.setLatestSetting(setting);
		} else {
			throw new CompensationRuntimeException(messages.getMessage(
					"CalculateRule.get.notExist", "该日期下的薪资公式不存在。"));
		}
		return rule;
	}

	public String createRuleName() {

		SalaryCalculateItem calItem = getCalItem();
		Date createTime = getCreated();

		Assert.notNull(calItem, messages.getMessage("规则所属的薪资计算项目不能为空。",
				"CalculateRule.createRuleName.calItemIsNull"));
		Assert.notNull(calItem, messages.getMessage("规则的创建时间不能为空。",
				"CalculateRule.createRuleName.createdIsNull"));

		return calItem.getName()
				+ org.apache.commons.lang.time.DateFormatUtils.format(
						createTime, DATE_FORMAT_PATTERN);

	}

	public static CalculateRule getLatest(Long id) {
		return get(id, new Date());
	}

	public static List<CalculateRule> findBySalaryCalculateItem(
			SalaryCalculateItem salaryCalculateItem, Date queryDate) {
		return getCalculateRuleRepository().findBySalaryCalculateItem(
				salaryCalculateItem, queryDate);
	}

	public static Object translatorToObject(Set<CalculateRule> calculateRules) {
		return ruleTranslator.translatorToObject(calculateRules);
	}

	public static Reader translatorToReader(Set<CalculateRule> calculateRules) {
		return ruleTranslator.translatorToReader(calculateRules);
	}

	public static String translatorToString(Set<CalculateRule> calculateRules) {
		return ruleTranslator.translatorToString(calculateRules);
	}

	public static InputStream translatorToInputStream(
			Set<CalculateRule> calculateRules) {
		return ruleTranslator.translatorToInputStream(calculateRules);
	}

}
